import re
import base64

from django.utils import timezone
from django.conf import settings
from django.contrib.auth.hashers import check_password, make_password
from mongoengine import fields
from mongoengine.errors import ValidationError, DoesNotExist
from models.meta import BaseDocument, QuerySetNoCache, IdField, BaseEmbeddedDocument
from models.warn import WebReceivers
from cryptography.fernet import Fernet

CIPHER_SUITE = Fernet(base64.urlsafe_b64encode(settings.CRYPTO_KEY.encode()))
USERNAME_ERROR = "用户名应该是4-127字符，可以是邮箱、中文、英文以及数字、下划线"
PASSWORD_ERROR = "密码需由8-16位非中文字符组成，至少的包括数字、大写字母、小写字母以及标点符号中的两种"


class EncryptedStringField(fields.StringField):
    def to_mongo(self, value):
        # 在保存到数据库之前进行加密
        if value is not None:
            value = CIPHER_SUITE.encrypt(value.encode()).decode()
            if not isinstance(value, str):
                raise ValueError('Encrypted value is not a string')
        return value

    def to_python(self, value):
        # 在从数据库读取数据时进行解密
        if value is not None and value.startswith('gAAAAA'):
            value = CIPHER_SUITE.decrypt(value.encode()).decode()
        return super().to_python(value)


class OutputUrlChoices(object):
    # 认证方式
    AUTH_TYPE = (
        ("api_key", "api_key"),  # 目前只支持在header自定义key， value
        ("basic_auth", "basic_auth"),  # 使用basic auth 方式
        ("no_auth", "no_auth"),  # 不用认证
    )
    ADD_TO = (
        ("header", "header"),
        ("basic_auth", "basic_auth"),
        ("no_auth", "no_auth")
    )
    METHOD = (
        ("GET", "GET"),
        ("POST", "POST"),
    )


class Permissions(BaseDocument):
    """
    权限
    """

    permission_id = IdField(required=True, unique=True)
    path = fields.StringField(max_length=127)  # url, 正则字符串
    name = fields.StringField(unique=True, max_length=127)  # 权限名称
    tag = fields.StringField(max_length=63)
    desc = fields.StringField(max_length=255)  # 补充说明
    method = fields.StringField(choices=("POST", "GET", "PUT", "DELETE"))  # 访问方式
    module = fields.StringField(max_length=127)  # 所属模块

    meta = {
        'queryset_class': QuerySetNoCache,
        'indexes': [
            'path',
        ],
        'allow_inheritance': True,
        'collection': "scan_permissions",
        'primary_key': 'permission_id',
    }

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.__str__()


class UserGroups(BaseDocument):
    """
    用户组, 权限控制通过用户组描述
    """

    user_group_id = IdField(required=True, unique=True)
    group = fields.StringField(unique=True, max_length=63)
    permissions = fields.ListField()
    modules = fields.ListField()
    desc = fields.StringField(max_length=255)

    meta = {
        'queryset_class': QuerySetNoCache,
        'indexes': ['-created_at', 'group'],
        'ordering': ['-created_at'],
        'allow_inheritance': True,
        'collection': "scan_user_groups",
        'primary_key': 'user_group_id',
    }

    def __str__(self):
        return self.group

    def __repr__(self):
        return self.__str__()


class PreLoginInfo(BaseEmbeddedDocument):
    time = fields.DateTimeField()
    ip = fields.StringField(default='')
    country = fields.StringField(default='')
    city = fields.StringField(default='')


class User(BaseDocument):
    """
    用户
    """

    user_id = IdField(required=True, unique=True)
    username = fields.StringField(min_length=4, max_length=127)
    password = fields.StringField(min_length=8, max_length=127)
    pre_login_info = fields.EmbeddedDocumentField(PreLoginInfo, default=PreLoginInfo)  # 上次登录信息：time,ip,country,city
    cur_login_info = fields.EmbeddedDocumentField(PreLoginInfo, default=PreLoginInfo)  # 本次登录信息: time,ip,country,city
    group = fields.ListField()
    product = fields.StringField(min_length=4, max_length=127, default="wass")
    is_staff = fields.BooleanField(default=False)  # 能否登录管理平台
    is_superuser = fields.BooleanField(default=False)  # 是否是超级管理员
    is_freeze = fields.BooleanField(default=False)  # 账号冻结标志
    is_active = fields.BooleanField(default=True)  # 账号是否启用
    expired_time = fields.DateTimeField(blank=True)  # 账号过期时间
    balance = fields.IntField(default=0)  # 账户余额，威胁情报查询次数
    # 地址信息
    province = fields.StringField(blank=True)  # 省
    city = fields.StringField(blank=True)  # 市
    address = fields.StringField(blank=True)  # 具体地址
    # 电话信息
    phone = EncryptedStringField(default="", max_length=11, blank=True)  # 电话
    is_active_phone = fields.BooleanField(default=False)
    # 邮箱信息
    email = EncryptedStringField(blank=True)
    is_active_email = fields.BooleanField(default=False)
    # 身份证信息
    idcard_num = fields.StringField(max_length=18, blank=True)  # 身份证号
    real_name = EncryptedStringField(blank=True)
    is_active_identity = fields.BooleanField(
        default=False
    )  # 实名身份认证状态，True-已验证 False-未验证
    # 其他信息
    qq = fields.StringField(blank=True)
    wx = fields.StringField(blank=True)  # 绑定微信
    pw_timestamp = fields.DateTimeField(blank=True)  # 密码到期时间
    desc = fields.StringField(max_length=255, blank=True)  # 备注信息
    warn_settings = fields.DictField(blank=True)  # 告警信息
    mfa = fields.StringField(blank=True)  # 用户是否绑定MFA
    profile = fields.DictField(blank=True)  # 扩展信息
    department = fields.StringField(default='')  # 部门
    limit_targets = fields.ListField(default=[])  # 限制目标范围
    parent_id = IdField()  # 父账号id

    USERNAME_FIELD = "username"
    REQUIRED_FIELDS = ['password', 'email']

    meta = {
        'queryset_class': QuerySetNoCache,
        'indexes': ['-created_at', 'username'],
        'allow_inheritance': True,
        'ordering': ['-created_at'],
        'collection': "scan_users",
        'primary_key': 'user_id',
    }

    def __str__(self):
        return self.username

    def __repr__(self):
        return self.__str__()

    def check_simple_password(self, password: str):
        password = password.strip()
        if password == self.username:
            raise ValidationError("用户名和密码不能相同")
        is_error_password = False
        if len(password) > 16 or len(password) < 8:
            is_error_password = True
        if re.fullmatch(r"[0-9]{8,16}|[a-z]{8,127}|[A-Z]{8,127}", password):
            is_error_password = True
        if re.search(r"\s", password):
            is_error_password = True
        if re.search(r"[\u4e00-\u9fa5]", password):
            is_error_password = True
        if is_error_password:
            raise ValidationError(PASSWORD_ERROR)

    @classmethod
    def check_username(cls, username):
        try:
            fields.EmailField().validate(username)
        except ValidationError:
            if not re.fullmatch(r"\w{4,127}", username):
                raise ValidationError(USERNAME_ERROR)
        try:
            cls.objects.get(username=username)
        except DoesNotExist:
            return username
        else:
            raise ValidationError(f"{username}用户已经存在")

    def set_password(self, password):
        self.check_simple_password(password)
        self.password = make_password(password)
        self.save()
        return self

    def check_password(self, password):
        return check_password(password, self.password)

    @classmethod
    def _create_user(
        cls, username, password, email=None, create_superuser=False, **kwargs
    ):
        now = timezone.now()
        cls.check_username(username)
        if email is not None:
            try:
                email_name, domain_part = email.strip().split('@', 1)
            except ValueError:
                pass
            else:
                email = '@'.join([email_name, domain_part.lower()])
        groups = set(kwargs.pop("group", []))
        user_group = []
        for g in groups:
            if isinstance(g, str):
                try:
                    group = UserGroups.objects.get(group=g)
                except DoesNotExist:
                    raise ValidationError(f"没有角色{g}, 创建用户 {username} 失败")
                else:
                    user_group.append(group)
            elif isinstance(g, UserGroups):
                user_group.append(g)
        default_product = getattr(settings, "DEFAULT_PRODUCT", "")
        user = cls(
            username=username,
            email=email,
            group=user_group,
            product=default_product,
            created_at=now,
            updated_at=now,
            **kwargs,
        )
        user.set_password(password)
        if create_superuser:
            user.is_staff = True
            user.is_superuser = True
        user.save()
        return user

    @classmethod
    def create_user(cls, username, password, email=None, **kwargs):
        return cls._create_user(username, password, email, **kwargs)

    @classmethod
    def create_superuser(cls, username, password, email=None, **kwargs):
        WebReceivers(**{
            'contact_name': '默认告警联系人',
            'receiver_email': email if email else "admin@default.com",
            'receiver_type': 0,
        }).save()
        if not kwargs.get("group"):
            kwargs.update({"group": ["管理员"]})
        return cls._create_user(
            username, password, email, create_superuser=True, limit_targets=['*'], **kwargs
        )


class Tokens(BaseDocument):
    """
    二次开发输入API token
    """

    token_id = IdField(required=True, unique=True)
    token = fields.StringField(required=True, unique=True)
    user_id = IdField()
    is_active = fields.BooleanField(default=True)
    expired_time = fields.DateTimeField(blank=True)
    desc = fields.StringField(max_length=200, default="")
    meta = {
        'queryset_class': QuerySetNoCache,
        'indexes': [
            'user_id',
        ],
        'ordering': ['-created_at'],
        'allow_inheritance': True,
        'collection': "scan_tokens",
        'primary_key': 'token_id',
    }

    def __str__(self):
        return self.token

    def __repr__(self):
        return self.__str__()


class OutUrls(BaseDocument):
    """
    消息推送url管理
    """

    url_id = IdField(required=True, unique=True)
    user_id = IdField()
    url = fields.StringField(blank=True, max_length=512)
    desc = fields.StringField(max_length=200, default="")
    auth_type = fields.StringField(choices=OutputUrlChoices.AUTH_TYPE)
    add_to = fields.StringField(choices=OutputUrlChoices.ADD_TO)
    auth_key = fields.StringField(blank=True)
    auth_value = fields.StringField(blank=True)
    method = fields.StringField(choices=OutputUrlChoices.METHOD)
    is_active = fields.BooleanField(default=True)
    expired_time = fields.DateTimeField(blank=True)
    meta = {
        'queryset_class': QuerySetNoCache,
        'indexes': [
            'user_id',
        ],
        'ordering': ['-created_at'],
        'allow_inheritance': True,
        'collection': "scan_out_urls",
        'primary_key': 'url_id',
    }

    def __str__(self):
        return self.url

    def __repr__(self):
        return self.__str__()


class Modules(BaseDocument):
    """
    模块权限
    """

    module_id = IdField(required=True, unique=True)
    module_name = fields.StringField(max_length=127, unique=True)
    permissions = fields.ListField()
    desc = fields.StringField(max_length=255)
    meta = {
        'queryset_class': QuerySetNoCache,
        'indexes': [
            'module_name',
        ],
        'ordering': ['-created_at'],
        'allow_inheritance': True,
        'collection': "scan_modules",
        'primary_key': 'module_id',
    }

    def __str__(self):
        return self.module_name

    def __repr__(self):
        return self.__str__()
